package com.elitesland.system.service.impl;

import com.elitesland.core.base.PagingVO;
import com.elitesland.core.exception.BusinessException;
import com.elitesland.system.convert.SysUdcConvert;
import com.elitesland.system.entity.QSysUdcDO;
import com.elitesland.system.entity.SysUdcDO;
import com.elitesland.system.param.SysUdcQueryParam;
import com.elitesland.system.repo.SysUdcRepo;
import com.elitesland.system.repo.SysUdcRepoProc;
import com.elitesland.system.service.SysUdcService;
import com.elitesland.system.vo.SysUdcComboVO;
import com.google.common.collect.Lists;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * <pre>
 * [功能说明]
 * </pre>
 *
 * @author Mir
 * @date 2020/6/25
 */
@Service
@RequiredArgsConstructor
public class SysUdcServiceImpl implements SysUdcService {

    private final SysUdcRepo sysUdcRepo;

    private final JPAQueryFactory jpaQueryFactory;

    @Override
    public PagingVO<SysUdcDO> search(SysUdcQueryParam param) {
        val records = sysUdcRepo.findAll(SysUdcRepoProc.where(param), param.getPageRequest());

        return PagingVO.<SysUdcDO>builder()
                .total(records.getTotalElements())
                .records(records.getContent())
                .build();
    }

    @Override
    public Optional<SysUdcDO> findOne(String domainCode, String udcCode, String udcVal) {
        val code = QSysUdcDO.sysUdcDO;

        return sysUdcRepo.findOne(code.domainCode.eq(domainCode)
                .and(code.udcCode.eq(udcCode)
                        .and(code.udcVal.eq(udcVal))));
    }

    @Override
    public List<SysUdcComboVO> listSubCodeLike(String domain, String udcCode3, String udcName) {
        val udcCode = QSysUdcDO.sysUdcDO;
        SysUdcQueryParam sysUdcQueryParam = new SysUdcQueryParam();
        sysUdcQueryParam.setDomainCode(domain);
        sysUdcQueryParam.setUdcCode(udcCode3);
        sysUdcQueryParam.setUdcVal(udcName);

        val records = sysUdcRepo.findAll(SysUdcRepoProc.where3(sysUdcQueryParam));
        List<SysUdcDO> sysUdcDOS = Lists.newArrayList(records);

        List<SysUdcComboVO> roleVos = sysUdcDOS.stream().map(r -> {
            val vo = new SysUdcComboVO();
            vo.setDomainCode(r.getDomainCode());
            vo.setUdcCode(r.getUdcCode());
            vo.setUdcVal(r.getUdcVal());
            vo.setValDesc(r.getValDesc());
            return vo;
        }).collect(Collectors.toList());

        return roleVos;
    }

    @Override
    public List<SysUdcComboVO> listCodeLike(String domain, String udcCode3, String udcName) {
        val udcCode = QSysUdcDO.sysUdcDO;
        SysUdcQueryParam sysUdcQueryParam = new SysUdcQueryParam();
        sysUdcQueryParam.setDomainCode(domain);
        sysUdcQueryParam.setUdcCode(udcCode3);
        sysUdcQueryParam.setUdcVal(udcName);

        val records = sysUdcRepo.findAll(SysUdcRepoProc.where2(sysUdcQueryParam));
        List<SysUdcDO> sysUdcDOS = Lists.newArrayList(records);

        List<SysUdcComboVO> roleVos = sysUdcDOS.stream().map(r -> {
            val vo = new SysUdcComboVO();
            vo.setDomainCode(r.getDomainCode());
            vo.setUdcCode(r.getUdcCode());
            vo.setUdcVal(r.getUdcVal());
            vo.setValDesc(r.getValDesc());
            return vo;
        }).collect(Collectors.toList());

        return roleVos;
    }

    @Override
    public List<SysUdcComboVO> listCodeCombosValLike(String domain, String udc, String valDesc) {
        val udcCode = QSysUdcDO.sysUdcDO;

        val codes = jpaQueryFactory.select(udcCode)
                .from(udcCode)
                .where(udcCode.domainCode.eq(domain))
                .where(udcCode.udcCode.eq(udc))
                .where(udcCode.valDesc.like("%" + valDesc + "%")).fetch();

        if (CollectionUtils.isEmpty(codes)) {
            return new ArrayList<>();
        }
        return codes.stream().map(SysUdcConvert.INSTANCE::codeToComboVo).collect(Collectors.toList());
    }

    @Override
    public Optional<SysUdcDO> findOneById(Long id) {
        return sysUdcRepo.findById(id);
    }

    @Override
    @Caching(evict = {
            @CacheEvict(value = "udccodes", key = "'codecombos-' + #code.domainCode + '-' + #code.udcCode"),
            @CacheEvict(value = "udccodes", key = "'codemap-' + #code.domainCode + '-' + #code.udcCode")
    })
    public void createCode(SysUdcDO code) {
        val udcOpt = findOne(code.getDomainCode(), code.getUdcCode(), code.getUdcVal());
        if (udcOpt.isPresent()) {
            throw new BusinessException(code.getDomainCode() + ":" + code.getUdcCode() + ":" + code.getUdcVal() + ", 已经存在，请勿重复添加");
        }
        sysUdcRepo.save(code);
    }

    @Override
    @Caching(evict = {
            @CacheEvict(value = "udccodes", key = "'codecombos-' + #code.domainCode + '-' + #code.udcCode"),
            @CacheEvict(value = "udccodes", key = "'codemap-' + #code.domainCode + '-' + #code.udcCode")
    })
    public void updateCode(SysUdcDO code) {
        val codeOpt = findOne(code.getDomainCode(), code.getUdcCode(), code.getUdcVal());
        codeOpt.ifPresent(c -> {
                    code.setId(c.getId());
                    code.setCreateUserId(c.getCreateUserId());
                    code.setCreateTime(c.getCreateTime());

                    sysUdcRepo.save(code);
                }
        );
    }

    @Override
    @Cacheable(value = "udccodes", key = "'codecombos-' + #domain + '-' + #udc")
    public List<SysUdcComboVO> listCodeCombos(String domain, String udc) {
        val udcCode = QSysUdcDO.sysUdcDO;

        val codes = jpaQueryFactory.select(udcCode)
                .from(udcCode)
                .where(udcCode.domainCode.eq(domain))
                .where(udcCode.udcCode.eq(udc)).fetch();

        if (CollectionUtils.isEmpty(codes)) {
            return new ArrayList<>();
        }
        return codes.stream().map(SysUdcConvert.INSTANCE::codeToComboVo).collect(Collectors.toList());
    }

    @Override
    @Cacheable(value = "udccodes", key = "'codemap-' + #domain + '-' + #udc")
    public Map<String, String> getCodeMap(String domain, String udc) {
        val codes = listCodeCombos(domain, udc);
        return codes.stream().collect(Collectors.toMap(SysUdcComboVO::getUdcVal, SysUdcComboVO::getValDesc));
    }

    @Override
    public List<SysUdcComboVO> findByUdcValues(List<String> udc) {
        val udcCode = QSysUdcDO.sysUdcDO;

        List<SysUdcDO> sysUdcDOS = sysUdcRepo.findAllByUdcValIn(udc);


        List<SysUdcComboVO> roleVos = sysUdcDOS.stream().map(r -> {
            val vo = new SysUdcComboVO();
            vo.setDomainCode(r.getDomainCode());
            vo.setUdcCode(r.getUdcCode());
            vo.setUdcVal(r.getUdcVal());
            vo.setValDesc(r.getValDesc());
            return vo;
        }).collect(Collectors.toList());

        return roleVos;
    }
}
